package com.simple.mipc.entity;

import android.os.Bundle;
import android.text.TextUtils;

import com.simple.mipc.annotation.Param;
import com.simple.mipc.annotation.Res;
import com.simple.mipc.cast.ParamCast;
import com.simple.mipc.tools.TypeCastTool;
import com.simple.mipc.utils.MethodUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.Getter;

public class Executor {

    @Getter
    private String res;

    private Map<String, ? extends ParamCast> names;

    private Map<String, Class<?>> types;

    private Method method;

    private Object instance;

    public Executor(Method method, Object instance) {
        Res res = method.getDeclaredAnnotation(Res.class);
        check(res, Res.class);
        this.res = res.value();
        this.res = this.res.isEmpty() ? method.getName() : this.res;
        this.method = method;
        this.instance = instance;
        names = Arrays.stream(method.getParameters())
                .map(parameter -> parameter.getDeclaredAnnotation(Param.class))
                .collect(Collectors.toMap(Param::value,
                        param -> TypeCastTool.getInstance().getCast(param.typeCast()), (o, n) -> o, LinkedHashMap::new
                ));
        types = Arrays.stream(method.getParameters())
                .collect(Collectors.toMap(parameter -> {
                    Param param = parameter.getDeclaredAnnotation(Param.class);
                    return Objects.isNull(param) || TextUtils.isEmpty(param.value()) ? parameter.getName() : param.value();
                }, Parameter::getType));
    }

    public <T> T execute(Bundle bundle) {
        List<Object> objects = new ArrayList<>();
        names.forEach((s, paramCast) -> {
            Object object = bundle.get(s);
            Object cast = paramCast.castTo(object, types.get(s));
            objects.add(cast);
        });
        return MethodUtil.invoke(instance, method, objects.toArray());
    }

    public <T> T execute(Map<String, String> data) {
        List<Object> objects = new ArrayList<>();
        names.forEach((s, paramCast) -> {
            String object = data.get(s);
            Object cast = paramCast.castTo(object, types.get(s));
            objects.add(cast);
        });
        return MethodUtil.invoke(instance, method, objects.toArray());
    }

    public void check(Annotation annotation, Class<? extends Annotation> clazz) {
        if (Objects.isNull(annotation)) {
            throw new IllegalArgumentException(String.format("缺少必要的注解：%s", clazz));
        }
    }
}
